package implementation;


import interfaces.HostInterface;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import algortims.SortedMachine;
import started.ReadPropertiesHost;

import entility.Job;
import entility.JobImpl;
import entility.Machine;
import entility.Notification;

public class JobController extends Thread {

	public static SortedMachine[] threads;
	public static Queue<Job> jobs;
	public int threadSize;
	private boolean working;
	public int checkNumber = 0;
	public int threadCheckInfo = 0;
	public int timeOutToCheck;
	public int timeOutToSend;
	public int lastJobSizeSended = 0;
	public int executionTimes = 0;
	public long lastRegisterSize = 0;

	public Machine me;


	public HostInterface host;

	public static JobController uniqueInstance;

	private JobController(int threadSize, int timeOutCheck,int timeOutSend)
	{
		// TODO Auto-generated constructor stub
		this.host = HostImpl.getInstance();
		setPriority(8);
		threads =  new SortedMachine[threadSize];
		jobs = new ConcurrentLinkedQueue<Job>();
		this.threadSize = threadSize;
		this.setWorking(false); 
		for(int i=0; i<this.threadSize; i++)
		{
			SortedMachine t = new SortedMachine();
			threads[i] = t;
		}

		this.timeOutToCheck = timeOutCheck;
		this.timeOutToSend = timeOutSend;

	}

	public void add(Job job) 
	{
		setPriority(4);
		jobs.offer(job);
		this.working = true;
	}




	private void check() {
		// TODO Auto-generated method stub
		checkNumber++;
		executionTimes++;

		//System.out.println("Number of Jobs: " + this.getNumberOfJobs() + ", number of jobs Waiting: "+ this.getNumberOfJobsWaiting());
		
		if(executionTimes > 50)
		{
			synchronized (HostImpl.numbers) {
				long size = HostImpl.numbers.size();
				System.out.println(size + " registros processados");
				if(lastRegisterSize == size && size > 0)
				{
					HostImpl.sort();
				}
				lastRegisterSize = size;
				
				System.gc();
			}
			executionTimes = 0;
			
		}
		
		
		
		


		if( this.getNumberOfJobs()==0 && working  )
		{
			threadCheckInfo++;
			if(threadCheckInfo > 25)
			{
				HostImpl.sort();
				threadCheckInfo = 0;
				
			}
		}
		else
			threadCheckInfo = 0;

		try 
		{
			if( this.getNumberOfJobs() <= 0 && this.working)
			{
				synchronized (HostImpl.meMyselfandI) {
					Job job = new JobImpl(Notification.HOST);
					job.setMethod("quantityOfJobs");

					job.setData(HostImpl.meMyselfandI);
					List<String> data  = job.getData();
					data.add("0");
					job.setData(data);
					HostImpl.primaryServer.getMessage(job);
					this.working = false;

				}

			}
			for(int i = 0; i< this.threadSize; i++)
			{
				if(!threads[i].running && jobs.size() > 0)
				{
					Job job = jobs.poll();
					threads[i] = new SortedMachine(job.getHead(), job.getIntData());
					threads[i].start();
				}
			}

			if(checkNumber*this.timeOutToCheck > this.timeOutToSend && this.lastJobSizeSended != this.getNumberOfJobs() )
			{
				synchronized (HostImpl.meMyselfandI) {

					Job job = new JobImpl(Notification.HOST);
					job.setMethod("quantityOfJobs");
					job.setData(HostImpl.meMyselfandI);
					List<String> data  = job.getData();
					
					data.add(new Integer(this.getNumberOfJobs()).toString());
					job.setData(data);
					HostImpl.primaryServer.getMessage(job);
					checkNumber = 0;
					lastJobSizeSended = this.getNumberOfJobs();

				}

			}


		}
		catch(Exception e)
		{

		}
		finally{

			try {
				sleep(this.timeOutToCheck);
				this.check();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}


	public void run()
	{
		this.check();
	}


	public int getNumberOfJobs()
	{
		int jobsinthread = 0;
		for(SortedMachine t : threads)
		{
			if(t.running)
				jobsinthread++;
		}

		return jobsinthread+jobs.size();
	}


	public int getNumberOfJobsWaiting()
	{
		return jobs.size();
	}


	public boolean isWorking() {
		return working;
	}


	public void setWorking(boolean working) {
		this.working = working;
	}


	public static JobController getInstance() {

		if(uniqueInstance == null)
		{
			ReadPropertiesHost props = ReadPropertiesHost.GetInstance();
			int numberOfThreads = props.getNumberOfCores();

			JobController newJobController = new JobController(numberOfThreads, props.getTimeOutCheck(), props.getTimeOutSend());
			uniqueInstance = newJobController;
		}
		// TODO Auto-generated method stub
		return uniqueInstance;
	}

	public List<Job> getHalfMyJobs() {

		List<Job> jobsToShare = new ArrayList<Job>();

		for(int i = 0; i< getNumberOfJobsWaiting() /2; i++)
		{
			jobsToShare.add(  jobs.poll() );
		}
		return jobsToShare;
	}


}
